﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.AccessControl;
using System.Windows.Forms;

namespace Movies.CopyUtility
{
    class HelperFileDetailsGuide
    {

        private DTOFileMovementReport fileReport;
        private ProgressBar thisBas;

        delegate void SetProgressValue(int val);
        private void updateProgress(int value)
        {
            if (thisBas.InvokeRequired)
            {
                try
                {
                    SetProgressValue d = new SetProgressValue(updateProgress);
                    WinDisplayControllor.MDIParent.Invoke(d, new object[] { value });
                }
                catch (Exception ex)
                { HelperContentProvider.OmitWarning(ex); }
            }
            else
            {
                try
                {
                    thisBas.Value = Convert.ToInt32(value);
                }
                catch (Exception ex) { HelperContentProvider.OmitWarning(ex); }
            }
        }

        delegate void SetProgressMax(int val);
        private void updateProgressMax(int value)
        {
            if (thisBas.InvokeRequired)
            {
                SetProgressMax d = new SetProgressMax(updateProgressMax);
                WinDisplayControllor.MDIParent.Invoke(d, new object[] { value });
            }
            else
            {
                thisBas.Maximum = Convert.ToInt32(value);
            }
        }


        public HelperFileDetailsGuide(String sources)
        {
            fileReport = new DTOFileMovementReport();
            IterateAndGetNames(sources, null);
        }


        public HelperFileDetailsGuide(List<String> sources)
        {
            fileReport = new DTOFileMovementReport();
            foreach (String sourceDir in sources)
            {
                IterateAndGetNames(sourceDir, null);
            }
        }

        public HelperFileDetailsGuide(List<String[]> sources)
        {
            fileReport = new DTOFileMovementReport();
            foreach (String[] sourceDir in sources)
            {
                IterateAndGetNames(sourceDir[1], null);
            }
        }

        public static bool isWriteAllowed(String destination){
            bool isWriteAccess = false;
            try
            {
                AuthorizationRuleCollection collection = Directory.GetAccessControl(destination).GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
                foreach (FileSystemAccessRule rule in collection)
                {
                    if (rule.AccessControlType == AccessControlType.Allow)
                    {
                        isWriteAccess = true;
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                isWriteAccess = false;
                HelperContentProvider.OmitWarning(ex);
            }
            return isWriteAccess;
        }

        public DTOFileMovementReport setDestination(String destination)
        {

            fileReport.AvailbaleSize = 0.0;
            fileReport.IsDestinationWriteAllowed = false;

            if (Directory.Exists(destination))
            {

                bool isWriteAccess = isWriteAllowed(destination);
                fileReport.IsDestinationWriteAllowed = isWriteAccess;
                if (!isWriteAccess)
                {
                    return fileReport;
                }

                DirectoryInfo dirInfo = new DirectoryInfo(destination);

                DriveInfo info = new DriveInfo(destination);
                if (info.IsReady)
                {
                    Double availa = Convert.ToDouble(info.AvailableFreeSpace);
                    fileReport.AvailbaleSize = availa / (1024 * 1024);
                    fileReport.IsDestinationWriteAllowed = true;
                    fileReport.Destination = destination;
                }
            }
            return fileReport;

        }



        public DTOFileMovementReport getReport()
        {
            return fileReport;
        }



        private void IterateAndGetNames(String sourceDir, String baseName)
        {
            if (sourceDir.EndsWith("\\"))
            {
                sourceDir = sourceDir.Substring(0, sourceDir.Length - 1);
            }

            if (baseName == null)
            {
                baseName = sourceDir.Substring(0, sourceDir.LastIndexOf("\\") + 1);
            }


            if (Directory.Exists(sourceDir) || File.Exists(sourceDir))
            {
                FileInfo filesInfo = new FileInfo(sourceDir);
                if (Directory.Exists(sourceDir))
                {
                    String fileOrFolder = getNameAfterBase(sourceDir, baseName);
                    DTOFileMovementDetails cpInfo = new DTOFileMovementDetails(fileOrFolder, baseName, false);
                    //Console.WriteLine(baseName + " -> " + fileOrFolder + " -> [F]");
                    fileReport.ValidList.Add(cpInfo);
                    DirectoryInfo dirs = new DirectoryInfo(sourceDir);

                    foreach (DirectoryInfo dir in dirs.GetDirectories())
                    {
                        String dirInfo = dir.FullName;
                        IterateAndGetNames(dirInfo, baseName);
                    }
                    foreach (FileInfo file in dirs.GetFiles())
                    {
                        String fileInfo = file.FullName;
                        IterateAndGetNames(fileInfo, baseName);
                    }
                }
                else
                {

                    String fileOrFolder = getNameAfterBase(sourceDir, baseName);
                    DTOFileMovementDetails cpInfo = new DTOFileMovementDetails(fileOrFolder, baseName, true);
                    //Console.WriteLine(baseName + " -> " + fileOrFolder + " -> [T]");
                    fileReport.ValidList.Add(cpInfo);

                    Double size = Convert.ToDouble(filesInfo.Length);
                    size = size / (1024 * 1024);
                    fileReport.Size = fileReport.Size + size;
                }
            }
            else
            {
                String fileOrFolder = getNameAfterBase(sourceDir, baseName);
                DTOFileMovementDetails cpInfo = new DTOFileMovementDetails(fileOrFolder, baseName, false);
                fileReport.SkipList.Add(cpInfo);
                //Console.WriteLine(baseName + " -> " + fileOrFolder + " -> [S]");
            }
        }


        private String getNameAfterBase(String dir, String baseDir)
        {
            String reminder = "";
            if (dir.StartsWith(baseDir))
            {
                reminder = dir.Substring((baseDir.Length), dir.Length - (baseDir.Length));

            }
            else
            {
                reminder = dir;
            }
            return reminder;
        }


        public Double getTotleSize()
        {
            return fileReport.Size;
        }

        public String getName(String fullName)
        {
            String name = null;
            if (fullName != null)
            {
                if (fullName.EndsWith("\\") && fullName.Length > 1)
                {
                    fullName = fullName.Substring(0, fullName.Length - 1);
                }
                int lastIndex = fullName.LastIndexOf("\\") + 1;
                int len = fullName.Length;
                if (fullName.Length > 0 && lastIndex > 0)
                {
                    name = fullName.Substring(lastIndex, fullName.Length - lastIndex);
                }
            }
            return name;
        }


        public bool startCopy(ProgressBar bar)
        {
            if (fileReport.Destination != null && fileReport.IsDestinationWriteAllowed && fileReport.IsSizeAvailbale)
            {
                if (bar != null)
                {
                    thisBas = bar;
                    updateProgressMax(Convert.ToInt32(fileReport.Size) + 1);
                }
                String destinationBase = fileReport.Destination;
                foreach (DTOFileMovementDetails fileOrFolder in fileReport.ValidList)
                {
                    if ((File.Exists(fileOrFolder.BaseFoolder + fileOrFolder.Name)) || Directory.Exists(fileOrFolder.BaseFoolder + fileOrFolder.Name))
                    {
                        //Console.WriteLine("Type: " + (fileOrFolder.IsFile ? "File" : "Folder"));
                        if (fileOrFolder.IsFile)
                        {
                            //Console.WriteLine("Coping: " + destinationBase + fileOrFolder.Name);
                            copy(fileOrFolder.BaseFoolder + fileOrFolder.Name, destinationBase + fileOrFolder.Name);
                        }
                        else
                        {
                            //Console.WriteLine("Creating: " + destinationBase + fileOrFolder.Name);
                            Directory.CreateDirectory(destinationBase + fileOrFolder.Name);
                        }
                    }
                    else
                    {
                        fileReport.SkipList.Add(fileOrFolder);
                    }
                }
            }
            return false;
        }

        public bool startMove(ProgressBar bar)
        {

            if (fileReport.Destination != null && fileReport.IsDestinationWriteAllowed && fileReport.IsSizeAvailbale)
            {
                if (bar != null)
                {
                    thisBas = bar;
                    updateProgressMax(Convert.ToInt32(fileReport.Size) + 1);
                }
                String destinationBase = fileReport.Destination;
                List<String> deleteDirs = new List<String>();
                List<String> deleteFile = new List<String>();
                foreach (DTOFileMovementDetails fileOrFolder in fileReport.ValidList)
                {
                    if ((File.Exists(fileOrFolder.BaseFoolder + fileOrFolder.Name)) || Directory.Exists(fileOrFolder.BaseFoolder + fileOrFolder.Name))
                    {
                        //Console.WriteLine("Type: " + (fileOrFolder.IsFile ? "File" : "Folder"));
                        if (fileOrFolder.IsFile)
                        {
                            //Console.WriteLine("Coping: " + destinationBase + fileOrFolder.Name);
                            //DriveInfo sourceDrive = new DriveInfo(fileOrFolder.BaseFoolder);
                            //DriveInfo destinationDrive = new DriveInfo(destinationBase);
                            if (isSameDriveMove(fileOrFolder.BaseFoolder,destinationBase))
                            {
                                move(fileOrFolder.BaseFoolder + fileOrFolder.Name, destinationBase + fileOrFolder.Name);
                            }else if (copy(fileOrFolder.BaseFoolder + fileOrFolder.Name, destinationBase + fileOrFolder.Name))
                            {
                                deleteFile.Add(fileOrFolder.BaseFoolder + fileOrFolder.Name);
                            }
                            
                        }
                        else
                        {
                            //Console.WriteLine("Creating: " + fileOrFolder.BaseFoolder + fileOrFolder.Name);
                            Directory.CreateDirectory(destinationBase + fileOrFolder.Name);
                            deleteDirs.Add(fileOrFolder.BaseFoolder + fileOrFolder.Name);
                        }
                    }
                    else
                    {
                        fileReport.SkipList.Add(fileOrFolder);
                    }

                }

                foreach(String file in deleteFile){
                        File.Delete(file);
                }
                deleteDirs.Reverse();
                foreach (String folder in deleteDirs)
                {
                    if (Directory.GetDirectories(folder).Length == 0 && Directory.GetFiles(folder).Length == 0)
                    {
                        Directory.Delete(folder);
                    }
                }

            }
            return false;
        }

        private bool isSameDriveMove(String src, String dest)
        {
            return dest.StartsWith(src.Substring(0, 1));
        }

        byte[] buf = null;
        int mbCopied = 0;
        long byteCopied = 0;
        int mbSize = (1048576);

        private bool move(String srcfilename, String destfilename)
        {
            bool isSuccess = false;
            try 
            {
                File.Move(srcfilename, destfilename);
                isSuccess = true;
            }
            catch (Exception ex) { HelperContentProvider.OmitWarning(ex);  }
            if (isSuccess)
            {
                FileInfo sourceInfo = new FileInfo(destfilename);
                long size = sourceInfo.Length;
                int sizeRem = (Int32)(size % mbSize);
                int sizeReduced = (Int32)((size - sizeRem) / mbSize);
                mbCopied = mbCopied + sizeReduced;
                byteCopied = byteCopied + sizeRem;
                if (byteCopied >= mbSize)
                {
                    byteCopied = byteCopied - mbSize;
                    mbCopied = mbCopied + 1;
                }
                updateProgress(mbCopied);
            }
            return isSuccess;
        }

        private bool copy(String srcfilename, String destfilename)
        {

            if (System.IO.File.Exists(srcfilename) == false)
            {
                //Console.WriteLine("Could not find the Source file");
                return false;
            }
            if (buf == null)
            {
                buf = new byte[mbSize];
            }

            Stream src = File.OpenRead(srcfilename);// File.Open();
            Stream des = File.Open(destfilename, FileMode.Create);

            BinaryReader srcFile = new BinaryReader(src);
            BinaryWriter desFile = new BinaryWriter(des);


            while (true)
            {
                int sz = srcFile.Read(buf, 0, mbSize);
                if (sz <= 0)
                    break;
                desFile.Write(buf, 0, sz);
                byteCopied =byteCopied+ sz;
                if (byteCopied > mbSize)
                {
                    mbCopied = mbCopied + 1;
                    byteCopied = byteCopied - mbSize;
                    updateProgress(mbCopied);
                }
                if (sz < mbSize)
                    break; // eof reached
            }
            srcFile.Close();
            desFile.Close();
            return true;
        }

    }
}
